package com.nulldev.util.net.demo;

import java.net.DatagramPacket;
import java.util.UUID;

import com.nulldev.util.FinalBypass;
import com.nulldev.util.math.lNumber;
import com.nulldev.util.net.IPUtil;
import com.nulldev.util.net.experimental.udp2.Client;
import com.nulldev.util.net.experimental.udp2.Connection;
import com.nulldev.util.net.experimental.udp2.DataReceivedListener;
import com.nulldev.util.net.experimental.udp2.Message;
import com.nulldev.util.net.experimental.udp2.Server;
import com.nulldev.util.net.tcp.TCPCore;
import com.nulldev.util.net.tcp.client.TCPClient;
import com.nulldev.util.net.tcp.server.TCPServer;
import com.nulldev.util.net.udp.UDPClient;
import com.nulldev.util.net.udp.UDPServer;
import com.nulldev.util.other.Timer;
import com.nulldev.util.string.StringsUtil;
import com.nulldev.util.threading.legacy.ThreadExecutor;

public class TCPUDPBenchmark {

	public static void main(String[] args) throws Exception {
		final long limit = 1000L;
		final int len = 96;
		final int tcpTP = IPUtil.nextFreePort(),
				udpTP = IPUtil.nextFreePort(),
				udp2TP = IPUtil.nextFreePort();
		final FinalBypass<Boolean> fastTest = FinalBypass.createBypass(Boolean.class);
		final lNumber udpIn = lNumber.nil();
		final lNumber udpIn0 = lNumber.nil();
		final lNumber udp2In = lNumber.nil();
		final lNumber tcpIn = lNumber.nil();
		final Timer t = new Timer();
		final UDPServer udps = new UDPServer(udpTP, 128, 64000) {
			
			@Override
			public void ext_handle(DatagramPacket p, String data) {
				if (fastTest.getObject()) {
					udpIn0.addSelf(1L);
				}else {
					udpIn.addSelf(1L);
				}
				//System.out.println(t.ticks2s() + ", packets sent: " + udpIn);
			}
		};
		udps.useFastMode = false;
		udps.start();
		final UDPClient udpc = new UDPClient("localhost", udpTP, 128, 64000) {
			
			@Override
			public void handle_EXT(String data, DatagramPacket p) throws Exception {
			}
		};
		t.start();
		t.setDelay(1L);
		while (!t.hasReached(limit)) {
			try {
				udpc.reply(StringsUtil.randomString(len));
			} catch (OutOfMemoryError e) {
				System.err.println("TEST FAILED: OUT OF MEMORY");
				break;
			} catch (Exception e) {
			}
		}
		t.pause();
		t.reset();
		t.unpause();
		udps.close();
		udps.useFastMode = true;
		udps.start();
		fastTest.setObject(true);
		while (!t.hasReached(limit)) {
			try {
				udpc.reply(StringsUtil.randomString(len));
			} catch (OutOfMemoryError e) {
				System.err.println("TEST FAILED: OUT OF MEMORY");
				break;
			} catch (Exception e) {
			}
		}
		t.pause();
		t.reset();
		udps.close();
		udpc.close();
		//udpIn.setSelf(0L);
		final TCPServer tcps = new TCPServer("Test Server", "test.server", tcpTP) {
			
			@Override
			public void receive(TCPCore connection, String event, String payload, byte[] bytes, long length, final long readTime) {
				tcpIn.addSelf(1L);
				//System.out.println(t.ticks2s() + ", packets sent: " + udpIn);
			}
		};
		
		tcps.start();
		final TCPClient tcpc = new TCPClient("Test Client", "test.client") {
			
			@Override
			public void receive(String event, String payload, byte[] bytes, long count, final long readTime) {
			}
		};
		tcpc.updateHost("localhost", tcpTP);
		tcpc.start();
		System.out.println("====== fTCP ======");
		t.unpause();
		while (!t.hasReached(limit)) {
			try {
				tcpc.send(StringsUtil.randomString(len));
			} catch (OutOfMemoryError e) {
				System.err.println("TEST FAILED: OUT OF MEMORY");
				break;
			} catch (Exception e) {
			}
		}
		t.pause();
		t.reset();
		t.unpause();
		final Server udp2s = new Server(udp2TP, "localhost", 64);
		udp2s.registerServerListerner(new DataReceivedListener() {
			
			@Override
			public void receivedMessage(Message message, final Connection connection) {
				udp2In.addSelf(1L);
			}
			
			@Override
			public void disconnected(UUID connectionId) {
			}
			
			@Override
			public void connected(Connection connection) {
			}
		});
		udp2s.startServer();
		final Client udp2c = new Client(udp2TP, "localhost");
		udp2c.startClient();
		while (!t.hasReached(limit)) {
			try {
				udp2c.sendReliableDataObject(StringsUtil.randomString(len));
			} catch (OutOfMemoryError e) {
			} catch (Exception e) {
			}
		}
		t.pause();
		final lNumber udp2In0 = udp2In.clone();
		udp2In.setSelf(0L);
		t.reset();
		t.unpause();
		while (!t.hasReached(limit)) {
			try {
				udp2c.sendUnreliableDataObject(StringsUtil.randomString(len));
			} catch (OutOfMemoryError e) {
			} catch (Exception e) {
			}
		}
		t.pause();
		System.out.println("Packets sent in 1 second via nullUtil 1.0 UDP API: " + udpIn);
		System.out.println("Packets sent in 1 second via nullUtil 2.6 UDP API + Fast Mode: " + udpIn0);
		System.out.println("Packets sent in 1 second via nullUtil 2.0 fTCP API: " + tcpIn);
		System.out.println("Packets sent in 1 second via nullUtil 2.6 fUDP API (Type.RELIABLE): " + udp2In0);
		System.out.println("Packets sent in 1 second via nullUtil 2.6 fUDP API (Type.UNRELIABLE): " + udp2In);
//		try {
//			if (udpIn.moreThan(tcpIn)) {
//				System.out.println("Result: UDP is faster by " + (udpIn.divide(tcpIn.longValue())) + " times.");
//			}else {
//				System.out.println("Result: TCP is faster by " + (tcpIn.divide(udpIn.longValue())) + " times.");
//			}
//		}catch (Exception e) {}
		tcpc.requestDisconnect();
		tcpc.waitForClose(500L);
		tcps.close();
		System.exit(-1);
	}

}
